﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Net.Sockets;
using System.Net;
using CommServer.Common;

namespace CommServer.Network
{
    public class TcpServer
    {
        public event Action<TcpSession> OnReceive;
        public event Action<TcpSession> OnConnect;
        public event Action<TcpSession> OnDisConnect;

        private ConcurrentQueue<TcpSession> m_SessionPool = new ConcurrentQueue<TcpSession>();
        private List<TcpSession> m_Sessions = new List<TcpSession>();
        private Socket m_Socket;

        public ConcurrentQueue<TcpSession> SessionPool { get => m_SessionPool; set => m_SessionPool = value; }

        public TcpServer()
        {
            //初始化
            for (var i = 0; i < Config.GetIntValue("MaxConnections"); i++)
            {
                var session = new TcpSession();
                var buffer = new byte[Config.GetIntValue("SessionBufferSize")];
                session.SetBuffer(buffer, 0, buffer.Length);
                session.Completed += Asyn_Compete;
                m_SessionPool.Enqueue(session);
            }
        }
        public void CloseSession(TcpSession Session)
        {
            lock ((m_Sessions as ICollection).SyncRoot)
            {
                for (int i = 0; i < m_Sessions.Count; i++)
                {
                    if (m_Sessions[i] == Session)
                    {
                        Session.Close(m_SessionPool);
                        //GetSessionPool().Enqueue(Session);
                        m_Sessions.RemoveAt(i);
                        break;
                    }
                }
            }
        }
        public void CloseSession(string SN)
        {

        }
        public List<TcpSession> GetSessions()
        {
            return m_Sessions;
        }
        public ConcurrentQueue<TcpSession> GetSessionPool()
        {
            return m_SessionPool;
        }

        public bool Start(int Port, int backlog = 200)
        {
            try
            {
                Stop();
                m_Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                m_Socket.Bind(new IPEndPoint(IPAddress.Any, Port));
                m_Socket.Listen(backlog);
                Accept_Start(null);
                return true;
            }
            catch (Exception ex)
            {
                Log.Error("Start error:" + ex.Message);
                return false;
            }
        }
        public void Stop()
        {
            if (m_Socket == null)
            {
                return;
            }
            try
            {
                //session资源释放
                lock ((m_Sessions as ICollection).SyncRoot)
                {
                    foreach (var session in m_Sessions)
                    {
                        try
                        {
                            session.Close(m_SessionPool);
                            //m_SessionPool.Enqueue(session);
                        }
                        catch (Exception ex)
                        {
                            Log.Error("close session error:" + ex.Message);
                        }
                    }
                }
                lock ((m_Sessions as ICollection).SyncRoot)
                {
                    m_Sessions.Clear();
                }
                m_Socket.Dispose();
            }
            catch (Exception ex)
            {
                Log.Error("Stop error:" + ex.Message);
            }
            finally
            {
                m_Socket = null;
            }
        }



        private void Asyn_Compete(object sender, SocketAsyncEventArgs e)
        {
            try
            {
                switch (e.LastOperation)
                {
                    case SocketAsyncOperation.Accept:
                        Accept_Completed(e);
                        break;
                    case SocketAsyncOperation.Receive:
                        Recv_Completed(e);
                        break;
                }
            }
            catch (Exception ex)
            {
                Log.Error("Asyn_Compete error:" + ex.Message);
            }
        }

        private void Accept_Start(SocketAsyncEventArgs e)
        {
            if (e == null)
            {
                e = new SocketAsyncEventArgs();
                e.Completed += new EventHandler<SocketAsyncEventArgs>(Asyn_Compete);
            }
            else
            {
                // 重用前进行对象清理
                e.AcceptSocket = null;
            }

            //如果I/O操作同步完成,直接调用完成
            try
            {
                if (!m_Socket.AcceptAsync(e))
                {
                    Accept_Completed(e);
                }
            }
            catch (Exception ex)
            {
                Log.Error("Accept_Start:" + ex.Message);
                if (m_Socket != null)
                {
                    Accept_Start(e);
                }
            }
        }
        private void Accept_Completed(SocketAsyncEventArgs e)
        {
            try
            {
                if (e.SocketError == SocketError.Success)
                {
                    if (m_SessionPool.TryDequeue(out TcpSession session))
                    {
                        session.Closed = false;
                        session.AcceptSocket = e.AcceptSocket;
                        IPEndPoint ip = (IPEndPoint)e.AcceptSocket.RemoteEndPoint;
                        session.SessionId = ip.Address.ToString() + ":" + ip.Port;
                        session.LastRecvProtocolFrameTime = DateTime.Now;
                        lock ((m_Sessions as ICollection).SyncRoot)
                        {
                            m_Sessions.Add(session);
                        }
                        OnConnect?.Invoke(session);

                        Recv_Start(session);

                    }
                    else
                    {
                        Log.Error("can not accept connections because out max connections");
                        e.AcceptSocket.Close();
                        Log.Error("Program Restarting~");
                        Program.restart();
                    }
                }
                else
                {
                    Log.Error("Accept_Completed SocketError=:" + e.SocketError);
                }
            }
            catch (Exception ex)
            {
                Log.Error("Accept_Completed error:" + ex.Message);
            }
            finally
            {
                if (m_Socket != null)
                {
                    Accept_Start(e);
                }
            }
        }

        private void Recv_Start(SocketAsyncEventArgs e)
        {
            try
            {
                if (!e.AcceptSocket.ReceiveAsync(e))
                {
                    Recv_Completed(e);
                }
            }
            catch (Exception ex)
            {
                Log.Error("Recv_Start error:" + ex.Message);
            }

        }
        private void Recv_Completed(SocketAsyncEventArgs e)
        {
            TcpSession session = e as TcpSession;

            if (e.BytesTransferred > 0)
            {
                if (e.SocketError == SocketError.Success)
                {
                    int curRecvStartPos = session.DataBuffer.Count;
                    for (var i = 0; i < e.BytesTransferred; i++)
                    {
                        session.DataBuffer.Add(e.Buffer[e.Offset + i]);
                    }
                    try
                    {
                        OnReceive?.Invoke(session);
                    }
                    catch (Exception ex)
                    {
                        Log.Error("OnReceive error:" + ex.Message + "|" + ex.StackTrace);
                    }
                    if ((e != null) && (e.AcceptSocket != null) && (e.AcceptSocket.Connected))
                        Recv_Start(e);
                }
                else
                {
                    Log.Error("OnReceive SocketError=" + e.SocketError);
                }
            }
            else
            {
                try
                {
                    lock ((m_Sessions as ICollection).SyncRoot)
                    {
                        m_Sessions.Remove(session);
                    }
                    session.Close(m_SessionPool);
                    Log.Info("session close normal ");
                }
                catch(Exception eee )
                {
                    Log.Error("session close error" + eee.Message + "|" + eee.StackTrace);
                }
                //m_SessionPool.Enqueue(session);
            }
        }
    }
}
